import { DataSyncWatcher } from './../../../../../common/dataSync/dataSyncWatcher';
import { SyncStatus } from './sync-status';
import { logger } from './../logger';
import { config } from '../config';
var util = require('util');
var path = require('path');
var lodash = require('lodash');
var throat = require('throat');
var async = require('async');
var events = require('events');

var utils = require('../utils');
var rest = require('../common/rest');
var authRest = require('./auth-rest');
var settings = require('./settings-service');
var teminal = require('./teminal-service');
var updates = require('./updates-service');
var resources = require('./resources-service');
var uploads = require('./uploads-service');

var teminalBindId = teminal.getTeminalBindId();
var throatedDownloadResource = throat(20, downloadResource);
var throatedUploadResource = throat(1, uploadResource);
var downloadUpdatesTimeout = 1 * 60 * 1000;
var downloadResourceTimeout = downloadUpdatesTimeout;
var uploadResourceTimeout = downloadUpdatesTimeout;

var _lastSyncTimeKey = undefined;
resources.eventEmitter.on('resources.toZero', function () {
    if (!config.checkResourceCount) return;
    SyncStatus.Current.setReset();
    _lastSyncTimeKey = 'all';
});

function uploadResources(errorHandler) {
    errorHandler = errorHandler || function (err, context) {
        logger.error(JSON.stringify(err));
        logger.error(err.stack);
        logger.error(JSON.stringify(context));
    };
    return uploads.queryAll().then(function (uploadList) {
        return Promise.all(lodash.map(uploadList, function (uploadObj) {
            var promise = null;
            if (uploadObj.syncpkg) {
                promise = uploads.queryChildren(uploadObj.syncpkg).then(function (children) {
                    return (<any>Promise).promisify(function (callback) {
                        async.eachSeries(children, function (child, acb) {
                            return throatedUploadResource(child).callback(function (err) {
                                if (err) {
                                    err.target = child;
                                }
                                acb(err);
                            });
                        }, function (err) {
                            if (err) {
                                return callback(err);
                            }
                            return throatedUploadResource(uploadObj).callback(callback);
                        });
                    })();
                });
            } else {
                promise = throatedUploadResource(uploadObj);
            }
            return promise.catch(function (err) {
                errorHandler(err, { upload: uploadObj });
            });
        }));
    });
}

function uploadResource(upload) {
    var headers = {};
    lodash.each(lodash.keys(upload.headers), function (key) {
        if (upload.headers[key] != undefined) {
            headers[key] = upload.headers[key];
        }
    });
    // 更换header中的bindId
    if (headers['mpj-sync-terminal-bind-id']) {
        headers['mpj-sync-terminal-bind-id'] = teminalBindId;
    }
    return authRest.put(rest.getUrl(upload.url), {
        json: upload.body,
        headers: headers,
        timeout: uploadResourceTimeout
    }).then(function () {
        return uploads.datastore.remove({ _id: upload._id });
    });
}

function uploadsIsEmpty() {
    return uploads.queryAll().then(function (uploads) {
        return uploads.length == 0 && DataSyncWatcher.Current.totalCounter == 0;
    });
}

function downloadsIsEmpty() {
    return updates.queryAll().then(function (updates) {
        return updates.length == 0 && DataSyncWatcher.Current.totalCounter == 0;
    });
}

function downloadUpdates() {
    return downloadPageUpdates().then(function (urls) {
        if (urls.length) {
            return downloadUpdates();
        }
    });
}
/**
 * download updates for only one page
 * @returns 返回的是要处理的updateUrls
 */
function downloadPageUpdates() {
    return fetchPageUpdates().then(function (result) {
        Contract.expect(result.urls);
        if (result.lastSyncTime) {
            return updates.saveUpdateUrls(result, SyncStatus.Current.isReady()).then(function (urls) {
                settings.lastSyncTimeKey = result.lastSyncTime;
                return result.urls;
            });
        } else {
            return Promise.resolve(result.urls);
        }
    });
}

function fetchPageUpdates() {
    var lastSyncTimeKey = _lastSyncTimeKey || settings.lastSyncTimeKey || 'all';
    var url = rest.getUrl(['api/stores', config.storeId, 'updates', lastSyncTimeKey], { excludedBindId: teminalBindId });
    _lastSyncTimeKey = undefined;
    return authRest.get(url, { timeout: downloadUpdatesTimeout });
}

/**
 * 取出当前需要下载全部的updateUrl，然后依次取每一个
 * TODO 出错三次后在一个时间范围内暂时不下了
 */
function downloadResources(errorHandler) {
    errorHandler = errorHandler || function (err, context) {
        logger.error(JSON.stringify(err));
        logger.error(err.stack);
        logger.error(JSON.stringify(context));
    };
    return updates.queryAll().then(function (updates) {
        return Promise.all(updates.map(function (update) {
            return throatedDownloadResource(update).catch(function (err) {
                errorHandler(err, { update: update });
            });
        }));
    });
}

/**
 * 针对一个updateURI
 * @param update
 * @returns {*}
 */
function downloadResource(originUpdate) {
    var update = lodash.clone(originUpdate);
    if (SyncStatus.Current.isPending()) {
        var currentTime = new Date().getTime();
        var timeWhen = update.timeWhen;
        if (timeWhen > currentTime) {
            timeWhen = currentTime;
        }
        update.timeWhen = timeWhen;
    } else {
        update.timeWhen = new Date().getTime();
    }
    if (update.state == 1) {
        return resources.logicDeleteById(update).then(function () {
            return updates.datastore.remove(originUpdate);
        });
    }
    return authRest.get(rest.getUrl([update.uri]), {}).then(function (resource) {
        return resources.updateResource(update, resource, false, false).then(function () {
            if (update.type == 'sync-packages') {
                return downloadPackageResource(update, resource);
            }
        });
    }).catch(function (res) {
        if (res && res.statusCode && res.statusCode >= 400 && res.statusCode < 500) {
            return resources.logicDeleteById(update, true);
        } else {
            throw res;
        }
    }).then(function () {
        return updates.datastore.remove(originUpdate);
    });
}

function downloadPackageResource(update, pkg) {
    return Promise.all(pkg.resources.map(function (pkgRes) {
        return authRest.get(rest.getUrl([pkgRes.uri]), { timeout: downloadResourceTimeout }).then(function (resource) {
            return resources.updateResource({
                updateId: resource.id,
                id: resource.id,
                state: update.state,
                type: pkgRes.type,
                uri: pkgRes.uri
            }, resource, true, false);
        });
    }));
}

function pendingCheck() {
    if (SyncStatus.Current.isPending()) {
        var promise = null;
        if (config.options.option.mode == 'c2l') {
            promise = downloadsIsEmpty();
        } else if (config.options.option.mode == 'l2c') {
            promise = uploadsIsEmpty();
        } else {
            promise = Promise.resolve(false);
        }
        return promise.then(function (ready) {
            if (ready) {
                SyncStatus.Current.setReady();
            }
        });
    }
    return Promise.resolve();
}

module.exports = {
    uploadResource: uploadResource,
    uploadResources: uploadResources,
    uploadsIsEmpty: uploadsIsEmpty,
    downloadsIsEmpty: downloadsIsEmpty,
    downloadUpdates: downloadUpdates,
    downloadPageUpdates: downloadPageUpdates,
    downloadResources: downloadResources,
    downloadResource: downloadResource,
    pendingCheck: pendingCheck
};
